#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "kernel.h"
//
#include "proto.h"
#include "scheduler.h"
#include "instrument.h"
#include "midi.h"

// this source file provides a simple MIDI event scheduler


#define MAXEVENTS   400

#define IN_ONE_GO    16       /* in one call to scheduler_set_ticker() */


static int tickspersecond = 4000;
static int ticker = 0;
static int eventread = 0, eventwrite = 0;
static SCHEDULEDEVENT events[MAXEVENTS];
static int interrupted = 0;


int scheduler_increment_ticker(int t, int de) {
  if (de)  return scheduler_set_ticker(ticker+(tickspersecond*t)/de);
  return scheduler_set_ticker(ticker+t);
}


int scheduler_set_ticker(int t) {

  int n;

  ticker = t;
  if (interrupted)  return 1;   // handle re-entrancy

  interrupted |= 1;
  n = 0;
  while ((events[eventread].ticker <= ticker) && (eventread != eventwrite) && (n < IN_ONE_GO)) {
    unsigned char *p;
    int available;

    p = (unsigned char *)&events[eventread].event0;
    midi_rx_bytes(NULL, p+1, *p, &available);
    n++;
    eventread++;
    if (eventread == MAXEVENTS)    eventread = 0;
  }
  interrupted &= ~1;

  return 0;
}


int scheduler_read_ticker() {
  return ticker;
}


void scheduler_flush_buffer() {
  eventread = eventwrite = 0;
}


void scheduler_set_tickspersecond(int ts) {
  tickspersecond = ts;
}


int scheduler_read_tickspersecond() {
  return tickspersecond;
}


int scheduler_info(int *available, int *first, int *last, int *size, int *tv, int *tps) {

  if (eventread <= eventwrite)
    *available = MAXEVENTS-1-(eventwrite-eventread);
  else
    *available = eventread-eventwrite-1;

  if (eventread == eventwrite)
    *first = *last = ticker;
  else {
    int ei;

    ei = eventwrite-1;
    if (ei < 0)  ei += MAXEVENTS;
    *first = events[eventread].ticker;
    *last = events[ei].ticker;
  }
  *size = MAXEVENTS;
  *tv = ticker;
  *tps = tickspersecond;

  return 0;
}


int schedule_event(int event0, int event1, int when) {
// returns -1 if failed, else returns amount of free space
  int available;

  if (eventread <= eventwrite)
    available = MAXEVENTS-1-(eventwrite-eventread);
  else
    available = eventread-eventwrite-1;

  if (!available)  return -1;

  interrupted |= 2;

  if (when == -2) {           // start of event-queue
    int wr;

    if (eventread == eventwrite) {
      when = ticker;
      wr = eventread;
      eventwrite++;
    } else {
      wr = eventread-1;
      if (wr < 0)    wr = MAXEVENTS-1;
      when = events[eventread].ticker;
    }
    events[wr].ticker = when;
    events[wr].event0 = event0;
    events[wr].event1 = event1;
    eventread = wr;

  } else if (when == -3) {    // end of event-queue
    if (eventread == eventwrite)
      when = ticker;
    else {
      int wr;

      wr = eventwrite-1;
      if (wr < 0)    wr = MAXEVENTS-1;
      when = events[wr].ticker;
    }
    events[eventwrite].ticker = when;
    events[eventwrite].event0 = event0;
    events[eventwrite].event1 = event1;
    eventwrite++;

  } else {
    events[eventwrite].ticker = when;
    events[eventwrite].event0 = event0;
    events[eventwrite].event1 = event1;
    eventwrite++;
  }

  if (eventwrite == MAXEVENTS)  eventwrite = 0;

  interrupted &= ~2;

  return available-1;
}
